#include <map>
#include <string>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <ctime> 
#include <cstdlib>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <pwd.h> 
#include <fcntl.h>
#include "myftpspec.h"
#include "session.h"

enum command_values { pwd, lpwd, cd, lcd, ls, lls, put, get, bye};
#define MAX_BUFF_SIZE 16384
char Buffer_Reader[MAX_BUFF_SIZE];

/* La fonction ci-dessous lit le nom de la machine locale*/
void name(char*s1){
    system("uname -n | cut -d ' ' -f2 > reponse.txt");     
    FILE * pFile;
    long lSize;
    //system("pwd > reponse.txt");
    pFile = fopen ( "reponse.txt" , "rw" );
    // obtain file size:
    fseek (pFile , 0 , SEEK_END);
    lSize = ftell (pFile);
    rewind (pFile);
    int c ;
    int k = 0;    
    while ((c = fgetc(pFile)) != EOF){
   	s1[k]=c;k++;
    }
   s1[k-1] = '\0';
   fclose(pFile);
   system("rm -f reponse.txt");
}

int generate_session_id() {
	srand((unsigned)time(0)); 
    int random_integer = rand();
	return random_integer;
}

char argument_buffer[BUFF_SIZE];

/*Execution de la commande pwd */
void pwd_command(CLIENT *m_client) {
	char **result;
	result = pwd_1(0, m_client);
	if (*result != 0) {
		std::cout << *result << std::endl;
	}
	else {
		clnt_perror(m_client, "remote pwd error");
	}
	return;
}

/*Execution de la commande lpwd */
void lpwd_command() {
	char local_buffer[BUFF_SIZE];
	getcwd(local_buffer, BUFF_SIZE);
	std::cout << local_buffer << std::endl;
	return;
}

/*Execution de la commande ls */
void ls_command(CLIENT *m_client) {
	char **result;
	
	result = ls_1(0, m_client);
	if (*result != 0) {
		std::cout << *result << std::endl;
	}
	else {
		clnt_perror(m_client, "remote ls error");
	}
	return;
}
/*Execution de la commande lls */
void lls_command() {
	DIR *dp;
	struct dirent *dirp;
	std::string dir_list;
	dp = opendir(".");
	if (dp != 0) {
		for(;;) {
			dirp = readdir(dp);
			if (dirp != 0) {
				dir_list += dirp->d_name;
				dir_list += "\n";
			}
			else {
				break;
			}
		}
		closedir(dp);
	}
	std::cout << dir_list.c_str() << std::endl;
	return;
}
/* Execution de la commande cd */
void cd_command(const std::string& repertoire,CLIENT *m_client) {
	char local_buffer[BUFF_SIZE];
	char *arg;
	bool_t *result_bool;
	char **result;
	strcpy(local_buffer, repertoire.c_str());
	arg = local_buffer;
	result_bool = cd_1(&arg, m_client);
	if (*result_bool == 0) {
		result = pwd_1(0, m_client);
		if (*result != 0) {
			std::cout << *result << std::endl;
		}
		else {
			clnt_perror(m_client, "remote pwd error");
		}
	}
	else {
		clnt_perror(m_client, "remote cd error");
	}
	
	return;
}

void get_command(const std::string& file_name, CLIENT *m_client){
      
    int *fd;
  	open_file fich;
  	FILE *fic;

  	fich.filename=(char*)file_name.c_str();
  	fich.rw=0;

  	// ouverture du fichier distant en lecture
  	fd = open_1(&fich,m_client);
  	if(fd == NULL){
    	printf("Probleme d'ouverture du fichier %s\n", fich.filename);
  	}

  	// ouverture du fichier local en ecriture
  	fic = fopen(fich.filename, "w");
  	if(fic == NULL){
    	printf("Probleme d'ouverture du fichier %s\n", fich.filename);
  	}

	if(*fd != -1){
    struct data_i *lecture = read_1(fd,m_client);    
    long lsize = lecture->len;
    while(lsize>4){
        fputs(lecture->buf,fic);
        lecture = read_1(fd,m_client);
        lsize = lecture->len;
       }
    }else
    {
    	printf("Impossible de trouver le fichier %s\n", fich.filename);
    }
  // fermeture du fichier de distant
  if(close_1(fd, m_client) == 0) {
      printf("Probleme de fermeture du fichier %s\n", fich.filename);
  }
  // fermeture du fichier locale
  if(fclose(fic) == EOF) {
      printf("Probleme de fermeture du fichier %s\n", fich.filename);
  }    
	    
}


void put_command(const std::string& file_name, CLIENT *m_client){
	   struct open_file fichier;
	    fichier.rw = 1;
	    fichier.filename = (char*)file_name.c_str();

	    //Ouverture du fichier
	    int *fd = open_1(&fichier,m_client);

	   	if(*fd != 0){
			long lSize;
			struct data_o write;
			FILE * pFile;
    		pFile = fopen(fichier.filename,"r+");    
            fseek (pFile , 0 , SEEK_END);
            lSize = ftell(pFile);
			rewind(pFile);
			
			//tempon de donnees a ecrite
			char buffer_writer [(int)lSize];
			write.buf = (char*) malloc(sizeof(char)*BUFF_SIZE);
			int c ;
    		int k = 0;    
    		while ((c = fgetc(pFile)) != EOF && k < MAX_BUFF_SIZE){
				buffer_writer[k] = c;
				k++;
   	 		}		
			buffer_writer[k] = '\0';

    		write.fd = *fd; //pas tres important 
			int t = 0;
			while(buffer_writer[t] != '\0'){
				int r = 0;
				for(r = 0;r<BUFF_SIZE && buffer_writer[t] != '\0';r++){
					write.buf[r] = buffer_writer[t];t++;
				}
				write.buf[r-1]='\0';
			
				int *m = write_1(&write,m_client); 
			}
			/*if(close_1(fd, m_client) == 0) {
      			("Probleme de fermeture du fichier distant %s\n", fichier.filename);
  			}
  			
  			if(fclose(pFile) == EOF) {
      			printf("Probleme de fermeture du fichier local %s\n", fichier.filename);
  			}	*/
	    } 
	    
}


void lcd_command(const std::string& repertoire) {
	char local_buffer[BUFF_SIZE];
	int result_code;
	
	result_code = chdir(repertoire.c_str());
	getcwd(local_buffer, BUFF_SIZE);
	std::cout << local_buffer << std::endl;
	return;
}

int main(int argc, char* argv[]) {
	CLIENT *client;
	char* arg;
	char USER[256];
	char** result;
	struct authunix_params *au;
	bool_t* result_bool;
	int error_code = 0;
	
	std::string input;
	std::string command;
	std::string args;
	std::string user_name;
	std::map<std::string, command_values> cmd;
	
	int session_id;
	
	name(USER);
	
	if (argc != 3) {
		std::cout << "Usage: " << argv[0] << " hostname" << "user  name "<<std::endl;
		error_code = 1;
	}
	else {
		client = clnt_create(argv[1], PROG, VERS, "tcp");
		
		if (client == 0){
			clnt_pcreateerror(argv[1]);
			error_code = 1;
		}
		else {
			
			struct user_parameter* param = get_param_1(&argv[2],client);
			
			if(param ->uid < 0){
				
				/* Si l'utilisateur n'est pas valide, on l'envoie dans le repertoire public */
				printf("Invalid user !!\n");
				printf("Redirect to public !! \n");
			}
			else{
				gid_t aup_gids[5];
				aup_gids[0]=(int)param->gid;
				int len;
				if ((len = getgroups(NGRPS, aup_gids)) < 0) {
					//printf("failed in getgroups()!!\n");
					exit(2);
				}else
				auth_destroy(client->cl_auth);
				len = (len > 16) ? 16 : len; 
				/* On fixe l'authentification du client*/
				//nouvelle mécanismes d'authentification
				//les arguments param->uid et param->gid sont respectivement le uid et le gid de 
				//l'utilisateur dont le non de compte a ete passe en qrgument
				
				client->cl_auth = (AUTH*) authunix_create(argv[1],(int)param->uid,(int)param->gid,len, aup_gids); 
				if (client->cl_auth == (AUTH *)NULL) {
					fprintf(stderr, "authunix_create failed!!\n");
				exit(1);
				}
			}
			cmd["pwd"]  = pwd;
			cmd["lpwd"] = lpwd;
			cmd["cd"] = cd;
			cmd["lcd"] = lcd;
			cmd["ls"] = ls;
			cmd["lls"] = lls;
			cmd["put"] = put;
			cmd["get"] = get;
			cmd["bye"] = bye;
			
			
			//assign_commands(commands);
			session_id = generate_session_id();
			
			while(true) {
				std::cout << USER << ":>";
				/* Lecture de la commande au clavier */
				std::getline(std::cin, input);
				//std::cout<<"Bonjour a toi" << input;
				std::istringstream iss(input, std::istringstream::in);
				if (iss) iss >> command;
				if (iss) iss >> args;
				//if (iss) iss >> user_name;

				switch (cmd[command]){
					case pwd: {
						pwd_command(client);
						break;
					}
					
					case cd: {
						cd_command(args,client);
						
						break;
					}
					
					case ls: {
						ls_command(client);
						break;
					}
					
					case lpwd: {
						lpwd_command();
						break;
					}
					
					case lcd: {				
						lcd_command(args);
						break;
					}
					
					case lls: {
						lls_command();
						break;
					}
					
					case put: {
						/* On verifie si l'utilisateur a le droit d'ecrire dans le repertoire */
						int *t = check_1((void *)NULL,client);
						if(*t != 0){
							put_command(args,client);
							break;
						}
						else{
							std::cout << "Vous n'avez pas les droits d'ecriture dans ce repertoire " << std::endl;
							std::cout << "Contacter votre administrateur " << std::endl;
							break;
						}
					}
					
					case get: {
						get_command(args,client);
						break;
					}
					case bye: {
						int *ter = bye_1((void *)NULL,client);            
            			if (ter == NULL){                
                    		clnt_perror(client,argv[1]);
                    		exit(1);
           				} 
           				exit(1);           
            			break;
					}
					default: {
						std::cout << "Commande inconnue " << std::endl;
						break;
					}
				}
			}
			
			clnt_destroy(client);
		}
	}
	
	return error_code;
}